#include "memory.h"
#include "bitmap.h"
#include "global.h"
#include "debug.h"
#include "stdint.h"
#include "print.h"
#include "string.h"
#include "sync.h"
#include "interrupt.h"

#define PG_SIZE 4096
#define PDE_IDX(addr)   ((addr & 0xffc00000) >> 22)
#define PTE_IDX(addr)   ((addr & 0x003ff000) >> 12)

/**位图地址
 * 0xc009f000是内核主线从栈顶,0xc009e000是内核主线程pcb
 * 一个页框大小的位图可表示4K*8*4KB=128MB, 位图位置安排在地址0xc009a000,
 * 这样本系统最大支持4个页框的位图,即512MB*/
#define MEM_BITMAP_BASE 0xc009a000
/*************************************************************/

/* 0xc0000000是内核从虚拟地址3G起, 0x100000指跨过低端1MB内存 */
#define K_HEAP_START 0xc0100000     //内核堆空间起始地址

/* 内存池结构,生成两个实例,管理内核用户内存池和用户内存池 */
struct pool {
    struct bitmap pool_bitmap;  //内存池用到的位图,管理物理内存
    uint32_t phy_addr_start;    //内存池管理的物理地址起始地址
    uint32_t pool_size;         //内存池字节容量
    struct lock lock;           //申请内存时互斥
};

struct pool kernel_pool, user_pool;
struct virtual_addr kernel_vaddr;   //用于给内核分配虚拟地址

struct arena {
    struct mem_block_desc *desc;    // 此arena关联的mem_block_desc
    uint32_t cnt;
    bool large; // large == true时,cnt表示页框数, 否则表示空闲的mem_block数量
};

struct mem_block_desc k_block_descs[DESC_CNT];  // 内核内存块描述符数组

static void vaddr_remove(enum pool_flags pf, void *_vaddr, uint32_t pg_cnt);

/** 在 pf 表示的虚拟内存池中申请 pg_cnt 个虚拟页，
 * 成功则返回虚拟页的起始地址，失败则返回 NULL */
static void* vaddr_get(enum pool_flags pf, uint32_t pg_cnt) {
    int vaddr_start = 0, bit_idx_start = -1;
    uint32_t cnt = 0;
    if (pf == PF_KERNEL){
        bit_idx_start = bitmap_scan(&kernel_vaddr.vaddr_bitmap, pg_cnt);
        if (bit_idx_start == -1)
            return NULL;
        while (cnt < pg_cnt) {
            bitmap_set(&kernel_vaddr.vaddr_bitmap, bit_idx_start + cnt++, 1);
        }
        vaddr_start = kernel_vaddr.vaddr_start + bit_idx_start * PG_SIZE;
    } else { //用户内存池
        struct task_struct *cur = running_thread();
        bit_idx_start = bitmap_scan(&cur->userprog_vaddr.vaddr_bitmap, pg_cnt);
        if (bit_idx_start == -1) {
            return NULL;
        }

        while (cnt < pg_cnt) {
            bitmap_set(&cur->userprog_vaddr.vaddr_bitmap, bit_idx_start + cnt++, 1);
        }
        vaddr_start = cur->userprog_vaddr.vaddr_start + bit_idx_start * PG_SIZE;

        // (0xc0000000 - PG_SIZE)作为用户3级栈在start_process被分配
        ASSERT((uint32_t)vaddr_start < (0xc0000000 - PG_SIZE));
    }
    return (void *)vaddr_start;
}

/* 得到虚拟地址vaddr对应的pte指针 */
uint32_t *pte_ptr(uint32_t vaddr){
    /** 先访问到页表自己
     * 再用页目录项pde(页目录内页表的索引)作为pte的索引访问到页表
     * 再用pte的索引作为页内偏移 */
    uint32_t *pte = (uint32_t *)(0xffc00000 + 
                                // 将1023作为虚拟地址前10位,找到第1023个页目录项,定位到页目录表,认为找到了一个页表,实际上是页目录表;
                                 ((vaddr & 0xffc00000) >> 10) +
                                // 虚拟地址中间10位取为vaddr的高10位,为正常访问vaddr时要定位的页目录项索引,即第几个页目录项,在这里变成页表的页表项索引,至此找到了vaddr所在的页表的物理地址
                                 PTE_IDX(vaddr) * 4);
                                //将vaddr中的页表项索引*4,作为最终的偏移地址,即在页表中,找到那个页表项. 此时并不取地址,因为此函数返回的就是这个地址值,所以类型装换后返回
    return pte;
}

/* 的到虚拟地址vaddr对应的pde指针 */
uint32_t *pde_ptr(uint32_t vaddr){
    /* 用0xfffff000访问页目录表地址,加上PDE_IDX*4,得到所在pde的指针 */
    uint32_t *pde = (uint32_t *)(0xfffff000 + PDE_IDX(vaddr) * 4);
    return pde;
}

/** 在m_pool指向的物理内存池中,分配1个物理页,
 * 成功返回页框物理地址,失败返回NULL */
static void *palloc(struct pool *m_pool){
    /* 扫描或设置位图要保证原子操作 */
    int bit_idx = bitmap_scan(&m_pool->pool_bitmap, 1); // 找一个物理页面
    if (bit_idx == -1)
        return NULL;
    bitmap_set(&m_pool->pool_bitmap, bit_idx, 1);       // 将此位置为1
    uint32_t page_phyaddr = ((bit_idx * PG_SIZE) + m_pool->phy_addr_start);
    return (void *)page_phyaddr;
}

/* 页表中添加虚拟地址_vaddr与物理地址_page_phyaddr的映射 */
static void page_table_add(void *_vaddr, void *_page_phyaddr){
    uint32_t vaddr = (uint32_t)_vaddr, page_phyaddr = (uint32_t)_page_phyaddr;
    uint32_t *pde = pde_ptr(vaddr);
    uint32_t *pte = pte_ptr(vaddr);

    // 先确保pde创建完成再执行*pte
    if (*pde & 0x00000001) {
        // 先判断目录项的P位,若为1,则表示该页表已存在
        ASSERT(!(*pte & 0x00000001));   // 页表项应该不存在
        if (!(*pte & 0x00000001)){
            // 创建页表项, 判断pte是否存在, 判断一下更放心
            *pte = (page_phyaddr | PG_US_U | PG_RW_W | PG_P_1);
        } else {
            PANIC("pte repeat");
            *pte = (page_phyaddr | PG_US_U | PG_RW_W | PG_P_1);
        }
    } else {
        // 页目录项pde不存在,要先创建页目录再再创建页表项
        /* 页表用到的页框一律从内存空间分配 */
        uint32_t pde_phyaddr = (uint32_t)palloc(&kernel_pool);
        *pde = (pde_phyaddr | PG_US_U | PG_RW_W | PG_P_1);

        /** 分配到的物理页地址pde_phyaddr对应的物理内存清0
         * 访问pde存储的页表起始物理地址, 用pte的无偏移,即前20位即可 */
        memset((void *)((int)pte & 0xfffff000), 0, PG_SIZE);

        ASSERT(!(*pte & 0x00000001));   //页表项应该不存在,刚刚全清零
        *pte = (page_phyaddr | PG_US_U | PG_RW_W | PG_P_1);
    }
}

/* 分配pg_cnt个页空间,成功则返回起始虚拟地址,失败返回NULL */
void *malloc_page(enum pool_flags pf, uint32_t pg_cnt){
    ASSERT(pg_cnt > 0 && pg_cnt < 3840);    // 3840页*4KB=15*1024*1024Bytes=15MB
    /** 分配malloc_page分为3步
     * 1 通过vaddr_get在虚拟内存池中申请虚拟地址
     * 2 通过palloc在物理内存池中申请物理页
     * 3 通过page_table_add将以上得到的虚拟地址和物理地址在页表中完成映射 */
    void *vaddr_start = vaddr_get(pf, pg_cnt);
    if (vaddr_start == NULL)
        return NULL;

    uint32_t vaddr = (uint32_t)vaddr_start, cnt = pg_cnt;
    struct pool *mem_pool = (pf & PF_KERNEL) ? &kernel_pool : &user_pool;

    /* 虚拟地址是连续的,但物理地址可能不连续,所以逐个映射 */
    while (cnt-- > 0) {
        void *page_phyaddr = palloc(mem_pool);
        if (page_phyaddr == NULL) {
            // 失败时要将曾经已申请的虚拟地址和物理页全部回滚, 在将来完成内存回收时再补充
            // TODO
            // 先回收已成功分配的页, 已分配了pg_cnt - cnt 个页
            /***************************************************/
            mfree_page(pf, vaddr_start, pg_cnt - cnt);
            // 再回收还没和物理地址绑定的cnt个虚拟页, 未建立映射， 所以不需要去除映射
            vaddr_remove(pf, (uint32_t *)vaddr, cnt); // vaddr是当前while中原准备绑定的虚拟地址
            /***************************************************/
            return NULL;
        }
        page_table_add((void *)vaddr, page_phyaddr);
        vaddr += PG_SIZE;
    }
    return vaddr_start;
}

/* 从物理内存池中申请pg_cnt页内存,成功返回其虚拟地址,失败返回NULL */
void *get_kernel_pages(uint32_t pg_cnt) {
    lock_acquire(&kernel_pool.lock);
    void *vaddr = malloc_page(PF_KERNEL, pg_cnt);
    if (vaddr != NULL) {
        // 分配的地址不为空, 将页框清0后返回
        memset(vaddr, 0, pg_cnt * PG_SIZE);
    }
    lock_release(&kernel_pool.lock);
    return vaddr;
}

/* 从用户内存池中申请pg_cnt页内存,成功返回其虚拟地址,失败返回NULL */
void *get_user_pages(uint32_t pg_cnt) {
    lock_acquire(&user_pool.lock);
    void *vaddr = malloc_page(PF_USER, pg_cnt);
    if (vaddr != NULL) {
        memset(vaddr, 0, pg_cnt * PG_SIZE);
    }
    lock_release(&user_pool.lock);
    return vaddr;
}

/* 将地址vaddr与pf池中的物理地址关联,申请vaddr映射的一页内存 */
void *get_a_page(enum pool_flags pf, uint32_t vaddr) {
    struct pool *mem_pool = pf & PF_KERNEL ? &kernel_pool : &user_pool;
    lock_acquire(&mem_pool->lock);

    // 先将虚拟地址对应的位图置1
    struct task_struct *cur = running_thread();
    int32_t bit_idx = -1;

    if (cur->pgdir != NULL && pf == PF_USER) {
        // 用户空间申请
        bit_idx = (vaddr - cur->userprog_vaddr.vaddr_start) / PG_SIZE;
        ASSERT(bit_idx > 0);
        bitmap_set(&cur->userprog_vaddr.vaddr_bitmap, bit_idx, 1);
    } else if (cur->pgdir == NULL && pf == PF_KERNEL) {
        // 内核空间申请
        bit_idx = (vaddr - kernel_vaddr.vaddr_start) / PG_SIZE;
        ASSERT(bit_idx > 0);
        bitmap_set(&kernel_vaddr.vaddr_bitmap, bit_idx, 1);
    } else {
        PANIC("get_a_page: not allow kernel alloc userspace or user alloc kernelspace by get_a_page");
    }

    void *page_phyaddr = palloc(mem_pool);
    if (page_phyaddr == NULL) {
        lock_release(&mem_pool->lock);
        return NULL;
    }
    page_table_add((void *)vaddr, page_phyaddr);

    lock_release(&mem_pool->lock);
    return (void *)vaddr;
}

/* 得到虚拟地址映射的物理地址 */
uint32_t addr_v2p(uint32_t vaddr) {
    uint32_t *pte = pte_ptr(vaddr);     // 页表项指针,*pte为物理地址页的起始地址
    return (((*pte) & 0xfffff000) + (vaddr & 0x00000fff));
}

/* 初始化内存池 */
static void mem_pool_init(uint32_t all_mem){
    put_str("   mem_pool_init start\n");
    uint32_t page_table_size = PG_SIZE * 256;
    // 1页的页目录表 + 0和756页目录项指向同一个页表 + 769~1022个页目录项共指向254个页表, 共256个页框
    uint32_t used_mem = page_table_size + 0x100000; //已使用的内存,包括页表大小和和低端的0x100000字节内存(1MB)
    uint32_t free_mem = all_mem - used_mem;
    uint16_t all_free_pages = free_mem / PG_SIZE;   //1页为4KB,不管总内存是不是4KB的倍数,不足一页不考虑

    uint16_t kernel_free_pages = all_free_pages / 2;
    uint16_t user_free_pages = all_free_pages - kernel_free_pages;

    /* 简化位图操作,余数不处理,坏处是会丢内存 */
    /* 好处是不用做内存越界检查,位图表示的内存小于物理内存 */
    uint32_t kbm_length = kernel_free_pages / 8;    //kernel bitmap的长度, 位图中一位表示一页, 以字节为单位
    uint32_t ubm_length = user_free_pages / 8;

    uint32_t kp_start = used_mem;   //kernel pool start,内核内存池的起始地址
    uint32_t up_start = kp_start + kernel_free_pages * PG_SIZE;   //user pool start,用户内存池的起始地址

    kernel_pool.phy_addr_start = kp_start;
    user_pool.phy_addr_start = up_start;

    kernel_pool.pool_size = kernel_free_pages * PG_SIZE;
    user_pool.pool_size = user_free_pages * PG_SIZE;

    kernel_pool.pool_bitmap.btmp_bytes_len = kbm_length;
    user_pool.pool_bitmap.btmp_bytes_len = ubm_length;

    /*********内核内存池和用户内存池位图***********
     * 位图是全局的数据，长度不固定。
     * 全局或静态的数组需要在编译时知道其长度，
     * 而我们需要根据总内存大小算出需要多少字节，
     * 所以改为指定一块内存来生成位图。
    *************************************************/
    // 内核使用的最高地址是 0xc009f000，这是主线程的栈地址（内核的大小预计为 70KB 左右）
    // 32MB 内存占用的位图是 2KB,内核内存池的位图先定在 MEM_BITMAP_BASE(0xc009a000)处
    kernel_pool.pool_bitmap.bits = (void *)MEM_BITMAP_BASE;

    /* 用户内存池的位图紧跟在内核内存池位图之后 */
    user_pool.pool_bitmap.bits = (void *)(MEM_BITMAP_BASE + kbm_length);
    /********************输出内存池信息**********************/
    put_str("       kernel_pool_bitmap_start:");put_int((int)kernel_pool.pool_bitmap.bits);
    put_str(" kernel_pool_phy_addr_start:");put_int(kernel_pool.phy_addr_start);
    put_str("\n");
    put_str("       user_pool_bitmap_start:");put_int((int)user_pool.pool_bitmap.bits);
    put_str(" user_pool_phy_addr_start:");put_int(user_pool.phy_addr_start);
    put_str("\n");

    /* 将位图置 0*/
    bitmap_init(&kernel_pool.pool_bitmap);
    bitmap_init(&user_pool.pool_bitmap);

    /* 下面初始化内核虚拟地址的位图，按实际物理内存大小生成数组。*/
    kernel_vaddr.vaddr_bitmap.btmp_bytes_len = kbm_length;// 用于维护内核堆的虚拟地址，所以要和内核内存池大小一致

    /* 位图的数组指向一块未使用的内存，目前定位在内核内存池和用户内存池之外*/
    kernel_vaddr.vaddr_bitmap.bits = (void *)(MEM_BITMAP_BASE + kbm_length + ubm_length);

    kernel_vaddr.vaddr_start = K_HEAP_START;
    bitmap_init(&kernel_vaddr.vaddr_bitmap);
    put_str("   mem_pool_init done\n");
}

/* 为malloc做准备 */
void block_desc_init(struct mem_block_desc *desc_array) {
    uint16_t desc_idx, block_size = 16;

    // 初始化每个mem_block_desc描述符
    for (desc_idx = 0; desc_idx < DESC_CNT; desc_idx++) {
        desc_array[desc_idx].block_size = block_size;

        // 初始化arena中的内存块数量
        desc_array[desc_idx].blocks_per_arena = (PG_SIZE - sizeof(struct arena)) / block_size;
        list_init(&desc_array[desc_idx].free_list);
        block_size *= 2; // 更新为下一个规格的内存块
    }
}

/* 返回arena中第idx个内存块的地址 */
static struct mem_block *arena2block(struct arena *a, uint32_t idx) {
    return ((struct mem_block *)((uint32_t)a + sizeof(struct arena) + idx * a->desc->block_size));
}

/* 返回内存块b所在的arena地址 */
static struct arena *block2arena(struct mem_block *b) {
    return ((struct arena *)((uint32_t)b & 0xfffff000));
}

/* 在堆中申请size字节内存 */
void *sys_malloc(uint32_t size) {
    enum pool_flags PF;
    struct pool *mem_pool;
    uint32_t pool_size;
    struct mem_block_desc *descs;
    struct task_struct *cur_thread = running_thread();

    // 判断用哪个内存池
    if (cur_thread->pgdir == NULL) {
        // 内核线程
        PF = PF_KERNEL;
        pool_size = kernel_pool.pool_size;
        mem_pool = &kernel_pool;
        descs = k_block_descs;
    } else {
        // 用户进程
        PF = PF_USER;
        pool_size = user_pool.pool_size;
        mem_pool = &user_pool;
        descs = cur_thread->u_block_desc;
    }

    /* 若申请的内存不在内存池容量范围内，则直接返回 NULL */
    if (!(size > 0 && size < pool_size)) {
        return NULL;
    }

    struct arena *a;
    struct mem_block *b;
    lock_acquire(&mem_pool->lock);
    
    // 超过最大内存块1024 分配页框
    if (size > 1024) {
        uint32_t page_cnt = DIV_ROUND_UP(size + sizeof(struct arena), PG_SIZE); // 向上取整需要的页框数

        a = malloc_page(PF, page_cnt);

        if (a != NULL) {
            memset(a, 0, page_cnt * PG_SIZE);
            a->desc = NULL;
            a->cnt = page_cnt;
            a->large = true;
            lock_release(&mem_pool->lock);
            return (void *)(a + 1); // 跨过(1个)arena大小,把剩下的内存返回
        } else {
            lock_release(&mem_pool->lock);
            return NULL;
        }
    } else {
        // 小于等于1024
        uint8_t desc_idx;

        /* 从内存块描述符中匹配合适的内存块规格 */
        for (desc_idx = 0; desc_idx < DESC_CNT; desc_idx++) {
            if (size <= descs[desc_idx].block_size) {
                break; // 从小往大找,找到退出
            }
        }

        // 若mem_block_desc 的 free_list 中已经没有可用的 mem_block，
        // 就创建新的 arena 提供 mem_block
        if (list_empty(&descs[desc_idx].free_list)) {
            a = malloc_page(PF, 1); // 分配一页作为arena
            if (a == NULL) {
                lock_release(&mem_pool->lock);
                return NULL;
            }

            memset(a, 0, PG_SIZE);
            // 对于分配的小块内存，将 desc 置为相应内存块描述符，
            // cnt 置为此 arena 可用的内存块数,large 置为 false
            a->desc = &descs[desc_idx];
            a->large = false;
            a->cnt = descs[desc_idx].blocks_per_arena;
            uint32_t block_idx;

            enum intr_status old_status = intr_disable();

            //开始将 arena 拆分成内存块，并添加到内存块描述符的 free_list 中
            for (block_idx = 0; block_idx < descs[desc_idx].blocks_per_arena; block_idx++) {
                b = arena2block(a, block_idx);
                ASSERT(!elem_find(&a->desc->free_list, &b->free_elem));
                list_append(&a->desc->free_list, &b->free_elem);
            }

            intr_set_status(old_status);
        }

        // 开始分配内存块
        b = elem2entry(struct mem_block, free_elem, list_pop(&(descs[desc_idx].free_list)));
        memset(b, 0, descs[desc_idx].block_size);

        a = block2arena(b); // 获取内存块b所在的arena
        a->cnt--;           // 空闲内存块数-1
        lock_release(&mem_pool->lock);
        return (void *)b;
    }
}

/* 将物理地址pg_phy_addr回收到物理内存池 */
static void pfree(uint32_t pg_phy_addr) {
    struct pool *mem_pool;
    uint32_t bit_idx = 0;
    if (pg_phy_addr >= user_pool.phy_addr_start) {
        // 用户物理内存池
        mem_pool = &user_pool;
        bit_idx = (pg_phy_addr - user_pool.phy_addr_start) / PG_SIZE;
    }
    else {
        // 内核物理内存池
        mem_pool = &kernel_pool;
        bit_idx = (pg_phy_addr - kernel_pool.phy_addr_start) / PG_SIZE;
    }
    bitmap_set(&mem_pool->pool_bitmap, bit_idx, 0);
}

/* 去掉页表中虚拟地址 vaddr 的映射，只去掉 vaddr 对应的 pte */
static void page_table_pte_remove(uint32_t vaddr) {
    uint32_t *pte = pte_ptr(vaddr);
    *pte &= ~PG_P_1;    // 将页表项pte的P位置0
    asm volatile("invlpg %0"
                 :
                 : "m"(vaddr)
                 : "memory");   // 更新tlb(页表高速缓存)
}

/* 在虚拟地址池中释放以_vaddr 起始的连续 pg_cnt 个虚拟页地址 */
static void vaddr_remove(enum pool_flags pf, void* _vaddr, uint32_t pg_cnt) {
    uint32_t bit_idx_start = 0, vaddr = (uint32_t)_vaddr, cnt = 0;

    if (pf == PF_KERNEL) {
        // 内核虚拟内存池
        bit_idx_start = (vaddr - kernel_vaddr.vaddr_start) / PG_SIZE;
        while (cnt < pg_cnt) {
            bitmap_set(&kernel_vaddr.vaddr_bitmap, bit_idx_start + cnt++, 0);
        }
    } else {
        // 用户虚拟内存池
        struct task_struct *cur_thread = running_thread();
        bit_idx_start = (vaddr - cur_thread->userprog_vaddr.vaddr_start) / PG_SIZE;
        while (cnt < pg_cnt) {
            bitmap_set(&cur_thread->userprog_vaddr.vaddr_bitmap, bit_idx_start + cnt++, 0);
        }
    }
}

/* 释放以虚拟地址 vaddr 为起始的 cnt 个物理页框 */
void mfree_page(enum pool_flags pf, void* _vaddr, uint32_t pg_cnt) {
    uint32_t pg_phy_addr;
    uint32_t vaddr = (uint32_t)_vaddr, cnt = 0;
    ASSERT(pg_cnt >= 1 && vaddr % PG_SIZE == 0);
    pg_phy_addr = addr_v2p(vaddr);  // 获取虚拟地址vaddr对应的物理地址

    /* 确保待释放的物理内存在 低端 1MB+1KB 大小的页目录+1KB 大小的页表地址范围外 */
    ASSERT((pg_phy_addr % PG_SIZE) == 0 && pg_phy_addr >= 0x102000);

    /* 判断 pg_phy_addr 属于用户物理内存池还是内核物理内存池 */

    if (pg_phy_addr >= user_pool.phy_addr_start) {
        //位于 user_pool 内
        vaddr -= PG_SIZE;
        while (cnt < pg_cnt) {
            vaddr += PG_SIZE;
            pg_phy_addr = addr_v2p(vaddr);

            /* 确保物理地址属于用户物理内存池 */
            ASSERT((pg_phy_addr % PG_SIZE) == 0 && pg_phy_addr >= user_pool.phy_addr_start);

            /* 先将对应的物理页框归还到内存池 */
            pfree(pg_phy_addr);

            /* 再从页表中清除此虚拟地址所在的页表项 pte */
            page_table_pte_remove(vaddr);

            cnt++;
        }
        /* 清空虚拟地址的位图中的相应位 */
        vaddr_remove(pf, _vaddr, pg_cnt);
    } else {
        //位于kernel_pool内
        vaddr -= PG_SIZE;
        while (cnt < pg_cnt) {
            vaddr += PG_SIZE;
            pg_phy_addr = addr_v2p(vaddr);

            /* 确保物理地址属于内核物理内存池 */
            ASSERT((pg_phy_addr % PG_SIZE) == 0 && pg_phy_addr >= kernel_pool.phy_addr_start && pg_phy_addr < user_pool.phy_addr_start);

            /* 先将对应的物理页框归还到内存池 */
            pfree(pg_phy_addr);

            /* 再从页表中清除此虚拟地址所在的页表项 pte */
            page_table_pte_remove(vaddr);

            cnt++;
        }
        /* 清空虚拟地址的位图中的相应位 */
        vaddr_remove(pf, _vaddr, pg_cnt);
    }
}

/* 回收内存 ptr */
void sys_free(void *ptr) {
    ASSERT(ptr != NULL);
    if (ptr != NULL) {
        enum pool_flags PF;
        struct pool *mem_pool;

        if (running_thread()->pgdir == NULL) {
            // 线程
            ASSERT((uint32_t)ptr >= K_HEAP_START);
            PF = PF_KERNEL;
            mem_pool = &kernel_pool;
        } else {
            // 进程
            PF = PF_USER;
            mem_pool = &user_pool;
        }

        lock_acquire(&mem_pool->lock);
        struct mem_block *b = ptr;
        struct arena* a = block2arena(b);   // 把 mem_block 转换成 arena，获取元信息

        ASSERT(a->large == 0 || a->large == 1);
        if (a->desc == NULL && a->large == true) {
            // 大于1024内存
            mfree_page(PF, a, a->cnt);
        } else {
            // 小于等于1024内存
            list_append(&a->desc->free_list, &b->free_elem);

            /* 再判断此 arena 中的内存块是否都是空闲，如果是就释放 arena */
            if (++a->cnt == a->desc->blocks_per_arena) {
                uint32_t block_idx;
                for (block_idx = 0; block_idx < a->desc->blocks_per_arena; block_idx++) {
                    // 脱离每个block
                    struct mem_block *b = arena2block(a, block_idx);
                    ASSERT(elem_find(&a->desc->free_list, &b->free_elem)); // 在freelist中
                    list_remove(&b->free_elem);
                }
                mfree_page(PF, a, 1); // 回收arena页框
            }
        }
        lock_release(&mem_pool->lock);
    }
}

/* 安装1页大小的vaddr，专门针对fork时，虚拟地址位图无需操作的情况 */
void *get_a_page_without_opvaddrbitmap(enum pool_flags pf, uint32_t vaddr) {
    struct pool *mem_pool = pf & PF_KERNEL ? &kernel_pool : &user_pool;
    lock_acquire(&mem_pool->lock);

    void *page_phyaddr = palloc(mem_pool);
    if (page_phyaddr == NULL) {
        lock_release(&mem_pool->lock);
        return NULL;
    }
    page_table_add((void *)vaddr, page_phyaddr);

    lock_release(&mem_pool->lock);
    return (void *)vaddr;
}

/* 内存管理部分初始化入口 */
void mem_init(void) {
    put_str("mem_init start\n");
    uint32_t mem_bytes_total = (*(uint32_t *)(0xb00));
    mem_pool_init(mem_bytes_total); // 初始化内存池
    block_desc_init(k_block_descs);

    lock_init(&user_pool.lock);
    lock_init(&kernel_pool.lock);
    put_str("mem_init done\n");
}